﻿using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace UDKDevKitVSXCore.Classes
{
    public abstract class IpcCommandListener
    {
        private CancellationTokenSource cts = new CancellationTokenSource();
        private string channelName;

        public IpcCommandListener(string channelName)
        {
            this.Dbg("created");

            this.channelName = channelName;

            this.StartTask();
        }

        public void StopListening()
        {
            cts.Cancel();
        }

        private void StartTask()
        {
            Task.Factory.StartNew(() => RMListener(), this.cts.Token);
        }

        private void RMListener()
        {
            this.cts.Token.ThrowIfCancellationRequested();

            this.Dbg("Started");

            var pipeServer = new NamedPipeServerStream(this.channelName, PipeDirection.In, 1);

            this.Dbg("WaitForConnection");

            pipeServer.WaitForConnection();

            this.Dbg("Has connection.");

            using (StreamReader streamReader = new StreamReader(pipeServer))
            {
                while (!streamReader.EndOfStream)
                {
                    try
                    {
                        if (this.cts.Token.IsCancellationRequested)
                        {
                            break;
                        }

                        // Read the request from the client. Once the client has
                        // written to the pipe its security token will be available.

                        string command = streamReader.ReadLine();

                        if (command.Length < 1000)
                        {
                            this.Dbg(String.Concat("command: ", command));
                        }
                        else
                        {
                            this.Dbg(String.Concat("command: ", command.Substring(0, 1000)));
                        }

                        if ("Close".Equals(command))
                        {
                            this.OnClose();
                            break;
                        }

                        this.OnProcessCommand(command);

                        if (String.IsNullOrEmpty(command))
                        {
                            // delay
                            System.Threading.Thread.Sleep(500);
                        }
                    }
                    catch (IOException e)
                    {
                        this.Dbg(String.Format("ERROR: {0}", e.Message));
                    }
                }

                this.Dbg("The connection is closed.");

                pipeServer.Close();
            }
        }

        protected abstract void OnProcessCommand(string command);

        protected abstract void OnClose();

        private void Dbg(string msg)
        {
            Debug.WriteLine(msg, String.Concat("PipeServer - ", this.channelName));
        }
    }
}
